"""Plugin manager for Dingo.

Copyright (C) Sarah Mount 2008.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
"""

from __future__ import absolute_import
import wx

__author__ = 'Sarah Mount <s.mount@wlv.ac.uk>'
__date__ = 'Arpil 2008'


class Plugin:
    """Superclass for Dingo plugins."""
    def __init__(self, name):
        self.name = name
        """@ivar: Name of this plugin. Used in menus, etc. so B{must} be short.
        @type: str
        """
        self.panel = None
        """@ivar: GUI panel available to this plugin.
        @type: Dingo.pluginManager.PluginPanel
        """
        self.graphPane = None
        """@ivar: reference to the network pane in the Dingo GUI.
        @type: Dingo.OGL.SensorCanvas
        """
        return

    def activate(self):
        """Activate this plugin.
        B{Must} be overridden by in C{Plugin} subclasses.
        """
        return

    def deactivate(self):
        """Deactivate this plugin.
        B{Must} be overridden by in C{Plugin} subclasses.
        """
        return

    def createMenu(self):
        """Create a menu which Dingo will add as a submenu in the Plugins menu of the GUI.
        May (optionally) be overridden by plugins.

        @rtype: wx.Menu
        """
        return None

    def createContextMenu(self):
        """Create a menu which will be available when users right-click on a network node.
        May (optionally) be overridden by plugins.

        @rtype: wx.Menu
        """
        return None

        

class PluginPanel(wx.Panel):
    """Panel added to the notebook page of the Dingo GUI.
    Each plugin has available one instance of C{PluginPanel}.
    """
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        return
    def CanPaste(self):
        """Return true if we can paste text selections into this panel.
        
        @rtype: bool
        """
        return False
    def GetSelectedText(self):
        """Return current text selection (or None).
        
        @rtype: str
        """
        return None
    

class PluginManager:
    def __init__(self, graph, nb, statusbar):
        self.plugins = {}
        """@ivar: All available plugins, indexed by name (type str).
        @type: dict
        """        
        self.graphPane = graph
        """@ivar: Reference to the network pane in the GUI.
        @type: Dingo.OGL.SensorGraph
        """        
        self.nb = nb
        """@ivar: Reference to the notebook in the Dingo GUI.
        @type: wx.Notebook
        """                
        self.status = statusbar
        """@ivar: Reference to the notebook in the Dingo GUI.
        @type: wx.StatusBar
        """                        
        self.pluginMenu = None
        """@ivar: Reference to the Plugin menu in the Dingo GUI.
        @type: wx.Menu
        """                
        self.pluginMenuIds = {}
        """@ivar: Dict containing ids (indexed by name) of each plugin in the Plugins menu in the main Dingo GUI. Used to manage plugin submenus.
        @type: dict
        """                
        self.pluginMenuSubmenus = {}
        """@ivar: Dict containing submenus for currently activated plugins.
        @type: dict
        """                
        # Load plugins
    	self._loadPlugins()
        return

    def _loadPlugins(self):
        """Load all plugins that can be found on disk.
	See L{Dingo.pluginManager.PluginManager.pluginDir}
        """
        if self.status is not None:
            self.status.SetStatusText('Plugin manager attempting to load plugins.', 1)
	from . import plugins
        import sys
        name = plugins.__name__ + '.'
        for mod in sys.modules.values():
            if mod is not None and mod.__name__.startswith(name):
            	for obj in mod.__dict__.values():
                    if isinstance(obj, Plugin):
                        self.plugins[obj.name] = obj
        return

    def getPluginNames(self):
        """Return a list of the names of all available plugins.
        Used by the L{Dingo.Main.DingoApp} class to create plugin menu.
        
        @rtype: list
        """        
        return self.plugins.keys()

    def activatePlugin(self, name):
        """Activate a particular plugin by name.
        Called by the L{Dingo.Main.DingoApp} class.

        @type name: str
        @param name: Name of the plugin to activate.
        """        
    	self.status.SetStatusText('PluginManager attempting to register plugin: ' + name, 0)
        self.plugins[name].graphPane = self.graphPane
        panel = PluginPanel(self.nb, -1)
        self.nb.AddPage(panel, name)
        panel.SetSize(self.nb.GetClientSize())
        panel.SetSizer(self.nb.GetSizer())
        self.plugins[name].panel = panel
        self.plugins[name].activate()
        # Deal with plugin menu
        menuitem = self.pluginMenu.FindItemById(self.pluginMenuIds[name])
        submenu =  self.plugins[name].createMenu()
        if submenu is not None:
            	self.status.SetStatusText('Got submenu from plugin' + name, 0)
        	item = self.pluginMenu.AppendMenu(wx.ID_ANY, name, submenu)
                self.pluginMenuSubmenus[name] = item
        else: self.pluginMenuSubmenus[name] = None
        return

    def deactivatePlugin(self, name):
        """Deactivate a particular plugin by name.
        Called by the L{Dingo.Main.DingoApp} class.

        @type name: str
        @param name: Name of the plugin to deactivate.
        """        
        self.status.SetStatusText('PluginManager attempting to deregister plugin: ' + name, 0)
        self.plugins[name].deactivate()
        for i in range(self.nb.GetPageCount()):
            if self.nb.GetPageText(i) == name:
                self.nb.DeletePage(i)
        if name in self.pluginMenuSubmenus and self.pluginMenuSubmenus[name] is not None:
            self.status.SetStatusText('Removing submenu from plugin' + name, 0)
            self.pluginMenu.RemoveItem(self.pluginMenuSubmenus[name])
        return

    def activateAllPlugins(self):
        """Activate all available plugins.
        Called by the L{Dingo.Main.DingoApp} class.
        """
        for plugin in self.plugins:
            self.activatePlugin(plugin)
        return

    def deactivateAllPlugins(self):
        """Deactivate all available plugins.
        Called by the L{Dingo.Main.DingoApp} class.
        """
        for plugin in self.plugins:
            self.deactivatePlugin(plugin)
        return

    def setMenu(self, menu, menuids):
        """Called by the L{Dingo.Main.DingoApp} class to ensure the plugin manager has access
        to the Plugins menu in the main GUI.
        """
        self.pluginMenu = menu
        self.pluginMenuIds = menuids
        return

